Functional PCA
fit_fdapca <- function(
df, time_column, value_column, replicate_column,
feat_column = NULL, feat = NULL,
cluster = FALSE, cmethod = "EMCluster", K = 2, filter = TRUE,
thresh = 0, num_nonzero = 10, clust_min_num_replicate = 10,
fpca_optns = NULL, fclust_optns = NULL)
{
if(!is.null(feat) & !is.null(feat_column)) {
df <- df %>%
filter_at(.vars = feat_column, any_vars((.) == feat))
}
y_lst <- plyr::dlply(df, replicate_column, function(x) x[[value_column]])
t_lst <- plyr::dlply(df, replicate_column, function(x) x[[time_column]])
if (filter){
idx <- sapply(y_lst, function(y){sum(abs(y) > thresh) >= num_nonzero})
y_lst <- y_lst[idx]
t_lst <- t_lst[idx]
}
if(length(y_lst) == 0) return(NULL)
feat_res <- NULL
if(cluster & (length(y_lst) >= clust_min_num_replicate)) {
feat_res <- try(fdapace::FClust(
y_lst, t_lst, k = K, optnsFPCA = fpca_optns, optnsCS = fclust_optns))
} else {
if(is.null(fpca_optns)) {
fpca_optns <- list()
}
if (length(y_lst) < clust_min_num_replicate) {
fpca_optns$dataType <- 'Sparse'
}
feat_res <- fdapace::FPCA(y_lst, t_lst, optns = fpca_optns)
if(cluster){
feat_res <- list("cluster" = rep(1, length(y_lst)),
"fpca" = feat_res, "clusterObj" = NULL)
}
}
feat_res
}
fitted_values_fpca <- function(obj, derOptns = list(p = 0))
{
selectedK <- NULL; clust_df <- NULL
if("cluster" %in% names(obj)){
clust_df <- data.frame(
"Replicate_ID" = names(obj$fpca$inputData$Ly),
"Cluster" = as.character(obj[["cluster"]]),
stringsAsFactors = FALSE)
obj <- obj[["fpca"]]
}
if(derOptns$p > 0) {
selectedK <- fdapace::SelectK(obj)$K
if(!is.finite(selectedK)) selectedK <- ncol(obj$xiEst)
}
fit <- fdapace:::fitted.FPCA(obj, K = selectedK, derOptns = derOptns)
rownames(fit) <- names(obj$inputData$Ly)
colnames(fit) <- obj$workGrid
fit <- reshape2::melt(fit, varnames = c("Replicate_ID", "time")) %>%
mutate(Replicate_ID = as.character(Replicate_ID))
if(!is.null(clust_df)){
fit <- suppressMessages(fit %>% left_join(clust_df))
}
return(fit)
}
fit_dist_to_baseline <- function(
dist_to_baseline, time_column="RelDay",
value_column = "dist_to_baseline", replicate_column = "Subject")
{
nGrid <- length(
seq(min(dist_to_baseline[[time_column]]),
max(dist_to_baseline[[time_column]])))
fpca_res <- fit_fdapca(
dist_to_baseline, time_column, value_column, replicate_column,
cluster = FALSE, filter = FALSE, fpca_optns = list(nRegGrid = nGrid))
fitted_mean <- data.frame(time = fpca_res$workGrid, value = fpca_res$mu)
fitted_response <- fitted_values_fpca(fpca_res) %>%
mutate(Subject = Replicate_ID) %>%
left_join(
fitted_values_fpca(fpca_res, derOptns = list(p = 1)) %>%
mutate(Subject = Replicate_ID) %>%
rename("deriv" = value)
)
return(list(res = fpca_res, mean = fitted_mean, fitted = fitted_response))
}
# this is because some subjects have multiple samples take the same day
bray_to_baseline_fltr <- bray_to_baseline %>%
group_by(perturbation, Subject, Group, Interval, RelDay) %>%
summarise(n = n(), dist_to_baseline = mean(dist_to_baseline)) %>%
ungroup()
bray_to_baseline_fltr %>% filter(n > 1)
Antibiotics
bray_to_baseline_fltr %>%
filter(perturbation == "Abx") %>%
filter(RelDay >= -50, RelDay <= 60) %>%
mutate(Interval = factor(Interval, level = names(abx_intv_cols))) %>%
ggplot(
aes(x = RelDay, y = dist_to_baseline,
group = Subject, color = Interval)) +
geom_line(aes(group = Subject), alpha = 0.7, lwd = 0.5) +
geom_point(alpha = 0.5, size = 1.2) +
scale_color_manual(values = abx_intv_cols) +
theme(legend.position = "bottom") +
guides(colour = guide_legend(override.aes = list(size=3))) +
xlab("Days from initial antibiotic dose") +
ylab("Bray-Curtis distance to 7 pre-antibiotic samples")

fpca.bray.abx <- fit_dist_to_baseline(
bray_to_baseline_fltr %>% filter(perturbation == "Abx", RelDay >= -50, RelDay <= 60))
save(list = c("fpca.bray.abx"), file = "output/fpca_res.rda")
(pAbx <- bray_to_baseline_fltr %>%
filter(perturbation == "Abx", RelDay >= -50, RelDay <= 60) %>%
ggplot(aes(x = RelDay, y = dist_to_baseline)) +
geom_line(
data = fpca.bray.abx[["fitted"]],
aes(group = Subject, x = time, y = value),
alpha = 0.3, size = 0.7, color = "grey30") +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
geom_point(aes(color = Interval), size = 1.5, alpha = 0.7) +
geom_line(
data = fpca.bray.abx[["mean"]], aes(x = time, y = value),
color = "navy", size = 2) +
scale_color_manual(values = abx_intv_cols, name = "Interval") +
scale_x_continuous(
name = "Days from initial antibiotic dose",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
scale_y_continuous(
name = "Bray-Curtis distance to baseline",
limits = c(0.1, 0.85), breaks = seq(0.1, 0.80, 0.1)) +
theme(text = element_text(size = 20)))

fpca.bray.abx[["fitted"]] <- fpca.bray.abx[["fitted"]] %>%
mutate(
Interval = ifelse(fpca.bray.abx[["fitted"]]$time < 0 , "PreAbx",
ifelse(fpca.bray.abx[["fitted"]]$time >= 0 & fpca.bray.abx[["fitted"]]$time <= 4, "MidAbx",
"PostAbx")))
(pAbxDeriv <- fpca.bray.abx[["fitted"]] %>%
ggplot(aes(x = RelDay)) +
geom_line(
aes(group = Subject, x = time, y = deriv, color = Interval),
alpha = 0.5, size = 0.7) +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
scale_color_manual(values = abx_intv_cols, name = "Interval") +
scale_x_continuous(name = "",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
ylab("Derivative"))

(pAbxLab <- bray_to_baseline_fltr %>%
filter(perturbation == "Abx", RelDay >= -50, RelDay <= 60) %>%
ggplot(aes(x = RelDay, y = dist_to_baseline)) +
geom_line(
data = fpca.bray.abx[["fitted"]],
aes(group = Subject, x = time, y = value),
alpha = 0.3, size = 0.7 ) +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
geom_text(
aes(label = Subject, color = Interval), size = 4, alpha = 0.7) +
geom_line(
data = fpca.bray.abx[["mean"]], aes(x = time, y = value),
color = "navy", size = 2) +
scale_color_manual(values = abx_intv_cols, name = "Interval") +
scale_x_continuous(
name = "Days from initial antibiotic dose",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
scale_y_continuous(
name = "Bray-Curtis distance to baseline",
limits = c(0.1, 0.85), breaks = seq(0.1, 0.80, 0.1)) +
theme(text = element_text(size = 20)))

# geom_point(
# data = abx_bray %>% filter(RelDay > StabTime),
# color = "orange", size = 2.5) +
vp = grid::viewport(width = 0.3, height = 0.32, x = 0.07, y =0.95, just = c("left", "top"))
print(pAbx)
print(pAbxDeriv + theme_bw(base_size = 10) + theme_subplot, vp = vp)

Diet
bray_to_baseline_fltr %>%
filter(perturbation == "Diet", RelDay >= -30, RelDay <= 30) %>%
mutate(Interval = factor(Interval, level = names(diet_intv_cols))) %>%
ggplot(
aes(x = RelDay, y = dist_to_baseline,
group = Subject, color = Interval)) +
geom_line(aes(group = Subject), alpha = 0.7, lwd = 0.5) +
geom_point(alpha = 0.5, size = 1.2) +
scale_color_manual(values = diet_intv_cols) +
theme(legend.position = "bottom") +
guides(colour = guide_legend(override.aes = list(size=3))) +
xlab("Days from diet initiation") +
ylab("Bray-Curtis distance to 7 pre-diet samples")

fpca.bray.diet30 <- fit_dist_to_baseline(
bray_to_baseline_fltr %>% filter(perturbation == "Diet", RelDay >= -30, RelDay <= 30))
save(list = c("fpca.bray.abx", "fpca.bray.diet", "fpca.bray.diet30"), file = "output/fpca_res.rda")
(pDiet <- bray_to_baseline_fltr %>%
filter(perturbation == "Diet", RelDay >= -30, RelDay <= 30) %>%
ggplot(aes(x = RelDay, y = dist_to_baseline)) +
geom_line(
data = fpca.bray.diet30[["fitted"]],
aes(group = Subject, x = time, y = value),
alpha = 0.3, size = 0.7, color = "grey30") +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
geom_point(aes(color = Interval), size = 1.5, alpha = 0.7) +
geom_line(
data = fpca.bray.diet30[["mean"]], aes(x = time, y = value),
color = "navy", size = 2) +
scale_color_manual(values = diet_intv_cols, name = "Interval") +
scale_x_continuous(
name = "Days from initial antibiotic dose",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
scale_y_continuous(
name = "Bray-Curtis distance to baseline",
limits = c(NA, NA), breaks = seq(0.1, 0.80, 0.1)) +
theme(text = element_text(size = 20)))

fpca.bray.diet30[["fitted"]] <- fpca.bray.diet30[["fitted"]] %>%
mutate(
Interval = ifelse(fpca.bray.diet30[["fitted"]]$time < 0 , "PreDiet",
ifelse(fpca.bray.diet30[["fitted"]]$time >= 0 & fpca.bray.diet30[["fitted"]]$time <= 4, "MidDiet",
"PostDiet")))
(pDietDeriv <- fpca.bray.diet30[["fitted"]] %>%
ggplot(aes(x = RelDay)) +
geom_line(
aes(group = Subject, x = time, y = deriv, color = Interval),
alpha = 0.5, size = 0.7) +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
scale_color_manual(values = diet_intv_cols, name = "Interval") +
scale_x_continuous(name = "",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
ylab("Derivative"))

(pDietLab <- bray_to_baseline_fltr %>%
filter(perturbation == "Diet", RelDay >= -50, RelDay <= 60) %>%
ggplot(aes(x = RelDay, y = dist_to_baseline)) +
geom_line(
data = fpca.bray.diet[["fitted"]],
aes(group = Subject, x = time, y = value),
alpha = 0.3, size = 0.7 ) +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
geom_text(
aes(label = Subject, color = Interval), size = 4, alpha = 0.7) +
geom_line(
data = fpca.bray.diet[["mean"]], aes(x = time, y = value),
color = "navy", size = 2) +
scale_color_manual(values = diet_intv_cols, name = "Interval") +
scale_x_continuous(
name = "Days from initial antibiotic dose",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
scale_y_continuous(
name = "Bray-Curtis distance to baseline",
limits = c(0.1, 0.85), breaks = seq(0.1, 0.80, 0.1)) +
theme(text = element_text(size = 20)))

vp = grid::viewport(width = 0.3, height = 0.32, x = 0.07, y =0.95, just = c("left", "top"))
print(pDiet)
print(pDietDeriv + theme_bw(base_size = 10) + theme_subplot, vp = vp)

Colon cleanout
bray_to_baseline_fltr %>%
filter(perturbation == "CC", RelDay >= -50, RelDay <= 50) %>%
mutate(Interval = factor(Interval, level = names(cc_intv_cols))) %>%
ggplot(
aes(x = RelDay, y = dist_to_baseline,
group = Subject, color = Interval)) +
geom_line(aes(group = Subject), alpha = 0.7, lwd = 0.5) +
geom_point(alpha = 0.5, size = 1.2) +
scale_color_manual(values = cc_intv_cols) +
theme(legend.position = "bottom") +
guides(colour = guide_legend(override.aes = list(size=3))) +
xlab("Days from diet initiation") +
ylab("Bray-Curtis distance to 7 pre-diet samples")

fpca.bray.cc30 <- fit_dist_to_baseline(
bray_to_baseline_fltr %>%
filter(perturbation == "CC", RelDay >= -30, RelDay <= 30) %>%
arrange(Subject, RelDay))
Joining, by = c("Replicate_ID", "time", "Subject")
save(list = c("fpca.bray.abx", "fpca.bray.diet","fpca.bray.diet30",
"fpca.bray.cc", "fpca.bray.cc30"),
file = "output/fpca_res.rda")
(pCC <- bray_to_baseline_fltr %>%
filter(perturbation == "CC", RelDay >= -30, RelDay <= 30) %>%
ggplot(aes(x = RelDay, y = dist_to_baseline)) +
geom_line(
data = fpca.bray.cc30[["fitted"]],
aes(group = Subject, x = time, y = value),
alpha = 0.3, size = 0.7, color = "grey30") +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
geom_point(aes(color = Interval), size = 1.5, alpha = 0.7) +
geom_line(
data = fpca.bray.cc30[["mean"]], aes(x = time, y = value),
color = "navy", size = 2) +
scale_color_manual(values = cc_intv_cols, name = "Interval") +
scale_x_continuous(
name = "Days from initial antibiotic dose",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
scale_y_continuous(
name = "Bray-Curtis distance to baseline",
limits = c(NA, NA), breaks = seq(0.1, 0.80, 0.1)) +
theme(text = element_text(size = 20)))

fpca.bray.cc30[["fitted"]] <- fpca.bray.cc30[["fitted"]] %>%
mutate(Interval = ifelse(fpca.bray.cc30[["fitted"]]$time < 0 , "PreCC","PostCC"))
(pCCDeriv <- fpca.bray.cc30[["fitted"]] %>%
ggplot(aes(x = RelDay)) +
geom_line(
aes(group = Subject, x = time, y = deriv, color = Interval),
alpha = 0.5, size = 0.7) +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
scale_color_manual(values = cc_intv_cols, name = "Interval") +
scale_x_continuous(name = "",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
ylab("Derivative"))

(pCCLab <- bray_to_baseline_fltr %>%
filter(perturbation == "CC", RelDay >= -50, RelDay <= 50) %>%
ggplot(aes(x = RelDay, y = dist_to_baseline)) +
geom_line(
data = fpca.bray.cc[["fitted"]],
aes(group = Subject, x = time, y = value),
alpha = 0.3, size = 0.7 ) +
geom_vline(xintercept = 0, lwd = 1, color = "orange") +
geom_vline(xintercept = 4, lwd = 1, color = "orange") +
geom_text(
aes(label = Subject, color = Interval), size = 4, alpha = 0.7) +
geom_line(
data = fpca.bray.cc[["mean"]], aes(x = time, y = value),
color = "navy", size = 2) +
scale_color_manual(values = cc_intv_cols, name = "Interval") +
scale_x_continuous(
name = "Days from initial antibiotic dose",
limits = c(NA, NA), breaks = seq(-50, 60, 10)) +
scale_y_continuous(
name = "Bray-Curtis distance to baseline",
limits = c(0.1, 0.85), breaks = seq(0.1, 0.80, 0.1)) +
theme(text = element_text(size = 20)))

vp = grid::viewport(width = 0.3, height = 0.32, x = 0.07, y =0.95, just = c("left", "top"))
print(pCC)
print(pCCDeriv + theme_bw(base_size = 10) + theme_subplot, vp = vp)

sessionInfo()
R version 3.5.1 (2018-07-02)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)
Matrix products: default
BLAS/LAPACK: /share/software/user/open/openblas/0.2.19/lib/libopenblasp-r0.2.19.so
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] fdapace_0.4.1 forcats_0.4.0 stringr_1.4.0 dplyr_0.8.3 purrr_0.3.2 readr_1.3.1
[7] tidyr_0.8.3 tibble_2.1.3 ggplot2_3.2.0 tidyverse_1.2.1.9000 RColorBrewer_1.1-2 phyloseq_1.26.1
loaded via a namespace (and not attached):
[1] nlme_3.1-140 fs_1.3.1 lubridate_1.7.4 httr_1.4.0 numDeriv_2016.8-1.1 tools_3.5.1
[7] backports_1.1.4 utf8_1.1.4 R6_2.4.0 vegan_2.5-5 rpart_4.1-15 Hmisc_4.2-0
[13] DBI_1.0.0 lazyeval_0.2.2 BiocGenerics_0.28.0 mgcv_1.8-28 colorspace_1.4-1 permute_0.9-5
[19] ade4_1.7-13 nnet_7.3-12 withr_2.1.2 tidyselect_0.2.5 gridExtra_2.3 compiler_3.5.1
[25] cli_1.1.0 rvest_0.3.4 Biobase_2.42.0 htmlTable_1.13.1 xml2_1.2.0 labeling_0.3
[31] scales_1.0.0 checkmate_1.9.4 digest_0.6.20 foreign_0.8-71 rmarkdown_1.14 XVector_0.22.0
[37] htmltools_0.3.6 base64enc_0.1-3 pkgconfig_2.0.2 dbplyr_1.4.2 htmlwidgets_1.3 rlang_0.4.0
[43] readxl_1.3.1 rstudioapi_0.10 generics_0.0.2 jsonlite_1.6 acepack_1.4.1 magrittr_1.5
[49] Formula_1.2-3 biomformat_1.10.1 Matrix_1.2-17 fansi_0.4.0 Rcpp_1.0.1 munsell_0.5.0
[55] S4Vectors_0.20.1 Rhdf5lib_1.4.3 ape_5.3 stringi_1.4.3 yaml_2.2.0 MASS_7.3-51.4
[61] zlibbioc_1.28.0 rhdf5_2.26.2 plyr_1.8.4 grid_3.5.1 parallel_3.5.1 crayon_1.3.4
[67] lattice_0.20-38 Biostrings_2.50.2 haven_2.1.1 splines_3.5.1 multtest_2.38.0 hms_0.5.0
[73] zeallot_0.1.0 knitr_1.23 pillar_1.4.2 igraph_1.2.4.1 reshape2_1.4.3 codetools_0.2-16
[79] stats4_3.5.1 reprex_0.3.0 glue_1.3.1 evaluate_0.14 latticeExtra_0.6-28 data.table_1.12.2
[85] modelr_0.1.4 vctrs_0.2.0 foreach_1.4.4 cellranger_1.1.0 gtable_0.3.0 assertthat_0.2.1
[91] xfun_0.8 broom_0.5.2 pracma_2.2.5 survival_2.44-1.1 iterators_1.0.10 IRanges_2.16.0
[97] cluster_2.1.0
LS0tCnRpdGxlOiAiRnVuY3Rpb25hbCBQQ0EiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmxpYnJhcnkoInBoeWxvc2VxIikKbGlicmFyeSgiUkNvbG9yQnJld2VyIikKbGlicmFyeSgidGlkeXZlcnNlIikKbGlicmFyeSgiZmRhcGFjZSIpCgpkYXRhZGlyIDwtICIuLi8uLi9kYXRhLyIKY3VyZGlyIDwtIGdldHdkKCkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCnRoZW1lX3VwZGF0ZSh0ZXh0ID0gZWxlbWVudF90ZXh0KDIwKSkKCnRoZW1lX3N1YnBsb3QgPC0gdGhlbWUoCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsY29sb3VyID0gTkEpLAogICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsY29sb3VyID0gTkEpCiAgICApCgphYnhfaW50dl9jb2xzIDwtIGMoIlByZUFieCIgPSAiZ3JleTYwIiwgIk1pZEFieCIgPSAiI0U0MUExQyIsIAogICAgICAgICAgICAgICAgICAgIlBvc3RBYngiID0gIiMwMEJGQzQiLCAiVW5wQWJ4IiA9ICJQdXJwbGUiKQpkaWV0X2ludHZfY29scyA8LSBjKCJQcmVEaWV0IiA9ICJncmV5NjAiLCAiTWlkRGlldCIgPSAiI0ZEOEQzQyIsIAogICAgICAgICAgICAgICAgICAgICJQb3N0RGlldCIgPSAiIzREQUY0QSIpCmNjX2ludHZfY29scyA8LSBjKCJQcmVDQyIgPSAiZ3JleTYwIiwgIlBvc3RDQyIgPSAiIzdBMDE3NyIpICMiI0FFMDE3RSIpCgppbnR2X2NvbHMgPC0gYyhhYnhfaW50dl9jb2xzLCBkaWV0X2ludHZfY29scywgY2NfaW50dl9jb2xzLCAiTm9JbnRlcnYiID0gImdyZXk2MCIpCmBgYAoKCgojIExvYWQgRGF0YQoKYGBge3J9CiMgRmlsZSBnZW5lcmF0ZWQgaW4gL3BlcnR1cmJhdGlvbl8xNnMvYW5hbHlzaXMvYW5hbHlzaXNfc3VtbWVyMjAxOS9nZW5lcmF0ZV9waHlsb3NlcS5ybWQKcHNTdWJqIDwtIHJlYWRSRFMoIi4uLy4uL2RhdGEvMTZTL3BoeWxvc2VxL3BlcnR1cmJfcGh5c2VxX3BhcnRpY2lwYW50c19kZWNvbnRhbV8xNUp1bDE5LnJkcyIpCnBzU3ViagojIG90dV90YWJsZSgpICAgT1RVIFRhYmxlOiAgICAgICAgIFsgMjQyNSB0YXhhIGFuZCA0NDAyIHNhbXBsZXMgXQojIHNhbXBsZV9kYXRhKCkgU2FtcGxlIERhdGE6ICAgICAgIFsgNDQwMiBzYW1wbGVzIGJ5IDQwIHNhbXBsZSB2YXJpYWJsZXMgXQpTTVAgPC0gZGF0YS5mcmFtZShzYW1wbGVfZGF0YShwc1N1YmopKQpTVUJKIDwtIFNNUCAlPiUgc2VsZWN0KFN1YmplY3QsIEFnZTpCaXJ0aFllYXIpICU+JSBkaXN0aW5jdCgpCmBgYAoKCmBgYHtyfQpsb2FkKCJvdXRwdXQvcGFpcndpc2VfZGlzdF90b19iYXNlbGluZV9zdWJqXzE2Uy5yZGEiKQpsb2FkKCJvdXRwdXQvZnBjYV9yZXMucmRhIikKbHMoKQpgYGAKCgojIEZ1bmN0aW9uYWwgUENBIAoKCmBgYHtyfQpmaXRfZmRhcGNhIDwtIGZ1bmN0aW9uKAogIGRmLCB0aW1lX2NvbHVtbiwgdmFsdWVfY29sdW1uLCByZXBsaWNhdGVfY29sdW1uLAogIGZlYXRfY29sdW1uID0gTlVMTCwgZmVhdCA9IE5VTEwsCiAgY2x1c3RlciA9IEZBTFNFLCBjbWV0aG9kID0gIkVNQ2x1c3RlciIsIEsgPSAyLCBmaWx0ZXIgPSBUUlVFLAogIHRocmVzaCA9IDAsIG51bV9ub256ZXJvID0gMTAsIGNsdXN0X21pbl9udW1fcmVwbGljYXRlID0gMTAsCiAgZnBjYV9vcHRucyA9IE5VTEwsIGZjbHVzdF9vcHRucyA9IE5VTEwpCnsKICBpZighaXMubnVsbChmZWF0KSAmICFpcy5udWxsKGZlYXRfY29sdW1uKSkgewogICAgZGYgPC0gZGYgJT4lIAogICAgICBmaWx0ZXJfYXQoLnZhcnMgPSBmZWF0X2NvbHVtbiwgYW55X3ZhcnMoKC4pID09IGZlYXQpKQogIH0KICB5X2xzdCA8LSBwbHlyOjpkbHBseShkZiwgcmVwbGljYXRlX2NvbHVtbiwgZnVuY3Rpb24oeCkgeFtbdmFsdWVfY29sdW1uXV0pCiAgdF9sc3QgPC0gcGx5cjo6ZGxwbHkoZGYsIHJlcGxpY2F0ZV9jb2x1bW4sIGZ1bmN0aW9uKHgpIHhbW3RpbWVfY29sdW1uXV0pCiAgaWYgKGZpbHRlcil7CiAgICBpZHggPC0gc2FwcGx5KHlfbHN0LCBmdW5jdGlvbih5KXtzdW0oYWJzKHkpID4gdGhyZXNoKSA+PSBudW1fbm9uemVyb30pCiAgICB5X2xzdCA8LSB5X2xzdFtpZHhdCiAgICB0X2xzdCA8LSB0X2xzdFtpZHhdCiAgfSAgCiAgaWYobGVuZ3RoKHlfbHN0KSA9PSAwKSByZXR1cm4oTlVMTCkKICBmZWF0X3JlcyA8LSBOVUxMCiAgaWYoY2x1c3RlciAmIChsZW5ndGgoeV9sc3QpID49IGNsdXN0X21pbl9udW1fcmVwbGljYXRlKSkgewogICAgZmVhdF9yZXMgPC0gdHJ5KGZkYXBhY2U6OkZDbHVzdCgKICAgICAgeV9sc3QsIHRfbHN0LCBrID0gSywgb3B0bnNGUENBID0gZnBjYV9vcHRucywgb3B0bnNDUyA9IGZjbHVzdF9vcHRucykpCiAgfSBlbHNlIHsKICAgIGlmKGlzLm51bGwoZnBjYV9vcHRucykpIHsKICAgICAgZnBjYV9vcHRucyA8LSBsaXN0KCkKICAgIH0gCiAgICBpZiAobGVuZ3RoKHlfbHN0KSA8IGNsdXN0X21pbl9udW1fcmVwbGljYXRlKSB7CiAgICAgIGZwY2Ffb3B0bnMkZGF0YVR5cGUgPC0gJ1NwYXJzZScKICAgIH0KICAgIGZlYXRfcmVzIDwtIGZkYXBhY2U6OkZQQ0EoeV9sc3QsIHRfbHN0LCBvcHRucyA9IGZwY2Ffb3B0bnMpCiAgICBpZihjbHVzdGVyKXsKICAgICAgZmVhdF9yZXMgPC0gbGlzdCgiY2x1c3RlciIgPSByZXAoMSwgbGVuZ3RoKHlfbHN0KSksIAogICAgICAgICAgICAgICAgICAgICAgICJmcGNhIiA9IGZlYXRfcmVzLCAiY2x1c3Rlck9iaiIgPSBOVUxMKQogICAgfQogIH0KICBmZWF0X3Jlcwp9CgpmaXR0ZWRfdmFsdWVzX2ZwY2EgPC0gZnVuY3Rpb24ob2JqLCBkZXJPcHRucyA9IGxpc3QocCA9IDApKSAKewogIHNlbGVjdGVkSyA8LSBOVUxMOyBjbHVzdF9kZiA8LSBOVUxMCiAgaWYoImNsdXN0ZXIiICVpbiUgbmFtZXMob2JqKSl7CiAgICBjbHVzdF9kZiA8LSBkYXRhLmZyYW1lKAogICAgICAiUmVwbGljYXRlX0lEIiA9IG5hbWVzKG9iaiRmcGNhJGlucHV0RGF0YSRMeSksCiAgICAgICJDbHVzdGVyIiA9ICBhcy5jaGFyYWN0ZXIob2JqW1siY2x1c3RlciJdXSksCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICAgIG9iaiA8LSBvYmpbWyJmcGNhIl1dCiAgfQogIGlmKGRlck9wdG5zJHAgPiAwKSB7CiAgICBzZWxlY3RlZEsgPC0gZmRhcGFjZTo6U2VsZWN0SyhvYmopJEsKICAgIGlmKCFpcy5maW5pdGUoc2VsZWN0ZWRLKSkgc2VsZWN0ZWRLIDwtIG5jb2wob2JqJHhpRXN0KQogIH0gIAogIGZpdCA8LSBmZGFwYWNlOjo6Zml0dGVkLkZQQ0Eob2JqLCBLID0gc2VsZWN0ZWRLLCBkZXJPcHRucyA9IGRlck9wdG5zKQogIHJvd25hbWVzKGZpdCkgPC0gbmFtZXMob2JqJGlucHV0RGF0YSRMeSkKICBjb2xuYW1lcyhmaXQpIDwtIG9iaiR3b3JrR3JpZAogIGZpdCA8LSByZXNoYXBlMjo6bWVsdChmaXQsIHZhcm5hbWVzID0gYygiUmVwbGljYXRlX0lEIiwgInRpbWUiKSkgJT4lCiAgICBtdXRhdGUoUmVwbGljYXRlX0lEID0gYXMuY2hhcmFjdGVyKFJlcGxpY2F0ZV9JRCkpCiAgaWYoIWlzLm51bGwoY2x1c3RfZGYpKXsKICAgIGZpdCA8LSBzdXBwcmVzc01lc3NhZ2VzKGZpdCAgJT4lIGxlZnRfam9pbihjbHVzdF9kZikpCiAgfQogIHJldHVybihmaXQpCn0KCgpmaXRfZGlzdF90b19iYXNlbGluZSA8LSBmdW5jdGlvbigKICBkaXN0X3RvX2Jhc2VsaW5lLCB0aW1lX2NvbHVtbj0iUmVsRGF5IiwgCiAgdmFsdWVfY29sdW1uID0gImRpc3RfdG9fYmFzZWxpbmUiLCByZXBsaWNhdGVfY29sdW1uID0gIlN1YmplY3QiKSAKewogIG5HcmlkIDwtIGxlbmd0aCgKICAgIHNlcShtaW4oZGlzdF90b19iYXNlbGluZVtbdGltZV9jb2x1bW5dXSksCiAgICAgICAgbWF4KGRpc3RfdG9fYmFzZWxpbmVbW3RpbWVfY29sdW1uXV0pKSkKICBmcGNhX3JlcyA8LSBmaXRfZmRhcGNhKAogICAgZGlzdF90b19iYXNlbGluZSwgdGltZV9jb2x1bW4sIHZhbHVlX2NvbHVtbiwgcmVwbGljYXRlX2NvbHVtbiwKICAgIGNsdXN0ZXIgPSBGQUxTRSwgZmlsdGVyID0gRkFMU0UsIGZwY2Ffb3B0bnMgPSBsaXN0KG5SZWdHcmlkID0gbkdyaWQpKQogIAogIGZpdHRlZF9tZWFuIDwtIGRhdGEuZnJhbWUodGltZSA9IGZwY2FfcmVzJHdvcmtHcmlkLCB2YWx1ZSA9IGZwY2FfcmVzJG11KQogIGZpdHRlZF9yZXNwb25zZSA8LSBmaXR0ZWRfdmFsdWVzX2ZwY2EoZnBjYV9yZXMpICU+JQogICAgbXV0YXRlKFN1YmplY3QgPSBSZXBsaWNhdGVfSUQpICU+JQogICAgbGVmdF9qb2luKAogICAgICBmaXR0ZWRfdmFsdWVzX2ZwY2EoZnBjYV9yZXMsIGRlck9wdG5zID0gbGlzdChwID0gMSkpICU+JQogICAgICAgIG11dGF0ZShTdWJqZWN0ID0gUmVwbGljYXRlX0lEKSAlPiUKICAgICAgICByZW5hbWUoImRlcml2IiA9IHZhbHVlKQogICAgKQogIHJldHVybihsaXN0KHJlcyA9IGZwY2FfcmVzLCBtZWFuID0gZml0dGVkX21lYW4sIGZpdHRlZCA9IGZpdHRlZF9yZXNwb25zZSkpCn0KYGBgCgoKYGBge3J9CiMgdGhpcyBpcyBiZWNhdXNlIHNvbWUgc3ViamVjdHMgaGF2ZSBtdWx0aXBsZSBzYW1wbGVzIHRha2UgdGhlIHNhbWUgZGF5CmJyYXlfdG9fYmFzZWxpbmVfZmx0ciA8LSBicmF5X3RvX2Jhc2VsaW5lICU+JSAKICBncm91cF9ieShwZXJ0dXJiYXRpb24sIFN1YmplY3QsIEdyb3VwLCBJbnRlcnZhbCwgUmVsRGF5KSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSwgZGlzdF90b19iYXNlbGluZSA9IG1lYW4oZGlzdF90b19iYXNlbGluZSkpICU+JQogIHVuZ3JvdXAoKQoKYnJheV90b19iYXNlbGluZV9mbHRyICU+JSBmaWx0ZXIobiA+IDEpCgpgYGAKCiMjIEFudGliaW90aWNzCgoKYGBge3IgYnJheS1hYnh9CmJyYXlfdG9fYmFzZWxpbmVfZmx0ciAlPiUgCiAgZmlsdGVyKHBlcnR1cmJhdGlvbiA9PSAiQWJ4IikgJT4lCiAgZmlsdGVyKFJlbERheSA+PSAtNTAsIFJlbERheSA8PSA2MCkgJT4lCiAgbXV0YXRlKEludGVydmFsID0gZmFjdG9yKEludGVydmFsLCBsZXZlbCA9IG5hbWVzKGFieF9pbnR2X2NvbHMpKSkgJT4lCiAgZ2dwbG90KAogICAgYWVzKHggPSBSZWxEYXksIHkgPSBkaXN0X3RvX2Jhc2VsaW5lLCAKICAgICAgICBncm91cCA9IFN1YmplY3QsIGNvbG9yID0gSW50ZXJ2YWwpKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IFN1YmplY3QpLCBhbHBoYSA9IDAuNywgbHdkID0gMC41KSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAxLjIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGFieF9pbnR2X2NvbHMpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsgCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MykpKSArCiAgeGxhYigiRGF5cyBmcm9tIGluaXRpYWwgYW50aWJpb3RpYyBkb3NlIikgKwogIHlsYWIoIkJyYXktQ3VydGlzIGRpc3RhbmNlIHRvIDcgcHJlLWFudGliaW90aWMgc2FtcGxlcyIpIApgYGAKCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpmcGNhLmJyYXkuYWJ4IDwtIGZpdF9kaXN0X3RvX2Jhc2VsaW5lKAogIGJyYXlfdG9fYmFzZWxpbmVfZmx0ciAlPiUgZmlsdGVyKHBlcnR1cmJhdGlvbiA9PSAiQWJ4IiwgUmVsRGF5ID49IC01MCwgUmVsRGF5IDw9IDYwKSkKCnNhdmUobGlzdCA9IGMoImZwY2EuYnJheS5hYngiKSwgZmlsZSA9ICJvdXRwdXQvZnBjYV9yZXMucmRhIikKYGBgCgpgYGB7ciBmcGNhLWJyYXktYWJ4LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Ni41fQoocEFieCA8LSBicmF5X3RvX2Jhc2VsaW5lX2ZsdHIgJT4lIAogIGZpbHRlcihwZXJ0dXJiYXRpb24gPT0gIkFieCIsIFJlbERheSA+PSAtNTAsIFJlbERheSA8PSA2MCkgJT4lCmdncGxvdChhZXMoeCA9IFJlbERheSwgeSA9IGRpc3RfdG9fYmFzZWxpbmUpKSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IGZwY2EuYnJheS5hYnhbWyJmaXR0ZWQiXV0sCiAgICBhZXMoZ3JvdXAgPSBTdWJqZWN0LCB4ID0gdGltZSwgeSA9IHZhbHVlKSwKICAgIGFscGhhID0gMC4zLCBzaXplID0gMC43LCBjb2xvciA9ICJncmV5MzAiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbHdkID0gMSwgY29sb3IgPSAib3JhbmdlIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQsIGx3ZCA9IDEsIGNvbG9yID0gIm9yYW5nZSIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IEludGVydmFsKSwgc2l6ZSA9IDEuNSwgYWxwaGEgPSAwLjcpICsKICBnZW9tX2xpbmUoCiAgICBkYXRhID0gZnBjYS5icmF5LmFieFtbIm1lYW4iXV0sIGFlcyh4ID0gdGltZSwgeSA9IHZhbHVlKSwKICAgIGNvbG9yID0gIm5hdnkiLCBzaXplID0gMikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhYnhfaW50dl9jb2xzLCBuYW1lID0gIkludGVydmFsIikgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIG5hbWUgPSAiRGF5cyBmcm9tIGluaXRpYWwgYW50aWJpb3RpYyBkb3NlIiwKICAgIGxpbWl0cyA9IGMoTkEsIE5BKSwgYnJlYWtzID0gc2VxKC01MCwgNjAsIDEwKSkgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSAiQnJheS1DdXJ0aXMgZGlzdGFuY2UgdG8gYmFzZWxpbmUiLCAKICAgIGxpbWl0cyA9IGMoMC4xLCAwLjg1KSwgYnJlYWtzID0gc2VxKDAuMSwgMC44MCwgMC4xKSkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkpCmBgYAoKCmBgYHtyIGZwY2EtZGVyaXYtYnJheS1hYngsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02LjV9CmZwY2EuYnJheS5hYnhbWyJmaXR0ZWQiXV0gPC0gZnBjYS5icmF5LmFieFtbImZpdHRlZCJdXSAlPiUKICBtdXRhdGUoCiAgICBJbnRlcnZhbCA9IGlmZWxzZShmcGNhLmJyYXkuYWJ4W1siZml0dGVkIl1dJHRpbWUgPCAwICwgIlByZUFieCIsCiAgICAgICAgICAgICAgIGlmZWxzZShmcGNhLmJyYXkuYWJ4W1siZml0dGVkIl1dJHRpbWUgPj0gMCAmIGZwY2EuYnJheS5hYnhbWyJmaXR0ZWQiXV0kdGltZSA8PSA0LCAiTWlkQWJ4IiwKICAgICAgICAgICAgICAgICAgICAgICJQb3N0QWJ4IikpKQoKKHBBYnhEZXJpdiA8LSAgZnBjYS5icmF5LmFieFtbImZpdHRlZCJdXSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBSZWxEYXkpKSArCiAgZ2VvbV9saW5lKAogICAgYWVzKGdyb3VwID0gU3ViamVjdCwgeCA9IHRpbWUsIHkgPSBkZXJpdiwgY29sb3IgPSBJbnRlcnZhbCksCiAgICBhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNykgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGx3ZCA9IDEsIGNvbG9yID0gIm9yYW5nZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBsd2QgPSAxLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGFieF9pbnR2X2NvbHMsIG5hbWUgPSAiSW50ZXJ2YWwiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiIiwKICAgIGxpbWl0cyA9IGMoTkEsIE5BKSwgYnJlYWtzID0gc2VxKC01MCwgNjAsIDEwKSkgKwogIHlsYWIoIkRlcml2YXRpdmUiKSkKYGBgCgoKYGBge3IgZnBjYS1icmF5LWFieC1sYWJzLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Ni41fQoocEFieExhYiA8LSBicmF5X3RvX2Jhc2VsaW5lX2ZsdHIgJT4lIAogIGZpbHRlcihwZXJ0dXJiYXRpb24gPT0gIkFieCIsIFJlbERheSA+PSAtNTAsIFJlbERheSA8PSA2MCkgJT4lCmdncGxvdChhZXMoeCA9IFJlbERheSwgeSA9IGRpc3RfdG9fYmFzZWxpbmUpKSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IGZwY2EuYnJheS5hYnhbWyJmaXR0ZWQiXV0sCiAgICBhZXMoZ3JvdXAgPSBTdWJqZWN0LCB4ID0gdGltZSwgeSA9IHZhbHVlKSwKICAgIGFscGhhID0gMC4zLCBzaXplID0gMC43ICkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGx3ZCA9IDEsIGNvbG9yID0gIm9yYW5nZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBsd2QgPSAxLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgZ2VvbV90ZXh0KAogICAgICBhZXMobGFiZWwgPSBTdWJqZWN0LCBjb2xvciA9IEludGVydmFsKSwgc2l6ZSA9IDQsIGFscGhhID0gMC43KSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IGZwY2EuYnJheS5hYnhbWyJtZWFuIl1dLCBhZXMoeCA9IHRpbWUsIHkgPSB2YWx1ZSksCiAgICBjb2xvciA9ICJuYXZ5Iiwgc2l6ZSA9IDIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYWJ4X2ludHZfY29scywgbmFtZSA9ICJJbnRlcnZhbCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gIkRheXMgZnJvbSBpbml0aWFsIGFudGliaW90aWMgZG9zZSIsCiAgICBsaW1pdHMgPSBjKE5BLCBOQSksIGJyZWFrcyA9IHNlcSgtNTAsIDYwLCAxMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gIkJyYXktQ3VydGlzIGRpc3RhbmNlIHRvIGJhc2VsaW5lIiwgCiAgICBsaW1pdHMgPSBjKDAuMSwgMC44NSksIGJyZWFrcyA9IHNlcSgwLjEsIDAuODAsIDAuMSkpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpKQoKICAjIGdlb21fcG9pbnQoCiAgIyAgICAgZGF0YSA9IGFieF9icmF5ICU+JSBmaWx0ZXIoUmVsRGF5ID4gU3RhYlRpbWUpLAogICMgICAgIGNvbG9yID0gIm9yYW5nZSIsIHNpemUgPSAyLjUpICsKYGBgCgpgYGB7ciBmcGNhLWJyYXktYWJ4LXZhbC1kZXJpdiwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTYuNX0KdnAgPSBncmlkOjp2aWV3cG9ydCh3aWR0aCA9IDAuMywgaGVpZ2h0ID0gMC4zMiwgeCA9IDAuMDcsIHkgPTAuOTUsIGp1c3QgPSBjKCJsZWZ0IiwgInRvcCIpKQpwcmludChwQWJ4KQpwcmludChwQWJ4RGVyaXYgKyB0aGVtZV9idyhiYXNlX3NpemUgPSAxMCkgKyB0aGVtZV9zdWJwbG90LCB2cCA9IHZwKQpgYGAKCgoKIyMgRGlldAoKCgpgYGB7cn0KYnJheV90b19iYXNlbGluZV9mbHRyICU+JSAKICBmaWx0ZXIocGVydHVyYmF0aW9uID09ICJEaWV0IiwgUmVsRGF5ID49IC0zMCwgUmVsRGF5IDw9IDMwKSAlPiUKICBtdXRhdGUoSW50ZXJ2YWwgPSBmYWN0b3IoSW50ZXJ2YWwsIGxldmVsID0gbmFtZXMoZGlldF9pbnR2X2NvbHMpKSkgJT4lCiAgZ2dwbG90KAogICAgYWVzKHggPSBSZWxEYXksIHkgPSBkaXN0X3RvX2Jhc2VsaW5lLCAKICAgICAgICBncm91cCA9IFN1YmplY3QsIGNvbG9yID0gSW50ZXJ2YWwpKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IFN1YmplY3QpLCBhbHBoYSA9IDAuNywgbHdkID0gMC41KSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAxLjIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGRpZXRfaW50dl9jb2xzKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArIAogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTMpKSkgKwogIHhsYWIoIkRheXMgZnJvbSBkaWV0IGluaXRpYXRpb24iKSArCiAgeWxhYigiQnJheS1DdXJ0aXMgZGlzdGFuY2UgdG8gNyBwcmUtZGlldCBzYW1wbGVzIikgCmBgYAoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmZwY2EuYnJheS5kaWV0MzAgPC0gZml0X2Rpc3RfdG9fYmFzZWxpbmUoCiAgYnJheV90b19iYXNlbGluZV9mbHRyICU+JSBmaWx0ZXIocGVydHVyYmF0aW9uID09ICJEaWV0IiwgUmVsRGF5ID49IC0zMCwgUmVsRGF5IDw9IDMwKSkKCnNhdmUobGlzdCA9IGMoImZwY2EuYnJheS5hYngiLCAiZnBjYS5icmF5LmRpZXQiLCAiZnBjYS5icmF5LmRpZXQzMCIpLCBmaWxlID0gIm91dHB1dC9mcGNhX3Jlcy5yZGEiKQpgYGAKCmBgYHtyIGZwY2EtYnJheS1kaWV0LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Ni41fQoocERpZXQgPC0gYnJheV90b19iYXNlbGluZV9mbHRyICU+JSAKICBmaWx0ZXIocGVydHVyYmF0aW9uID09ICJEaWV0IiwgUmVsRGF5ID49IC0zMCwgUmVsRGF5IDw9IDMwKSAlPiUKZ2dwbG90KGFlcyh4ID0gUmVsRGF5LCB5ID0gZGlzdF90b19iYXNlbGluZSkpICsKICBnZW9tX2xpbmUoCiAgICBkYXRhID0gZnBjYS5icmF5LmRpZXQzMFtbImZpdHRlZCJdXSwKICAgIGFlcyhncm91cCA9IFN1YmplY3QsIHggPSB0aW1lLCB5ID0gdmFsdWUpLAogICAgYWxwaGEgPSAwLjMsIHNpemUgPSAwLjcsIGNvbG9yID0gImdyZXkzMCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsd2QgPSAxLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNCwgbHdkID0gMSwgY29sb3IgPSAib3JhbmdlIikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gSW50ZXJ2YWwpLCBzaXplID0gMS41LCBhbHBoYSA9IDAuNykgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBmcGNhLmJyYXkuZGlldDMwW1sibWVhbiJdXSwgYWVzKHggPSB0aW1lLCB5ID0gdmFsdWUpLAogICAgY29sb3IgPSAibmF2eSIsIHNpemUgPSAyKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGRpZXRfaW50dl9jb2xzLCBuYW1lID0gIkludGVydmFsIikgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIG5hbWUgPSAiRGF5cyBmcm9tIGluaXRpYWwgYW50aWJpb3RpYyBkb3NlIiwKICAgIGxpbWl0cyA9IGMoTkEsIE5BKSwgYnJlYWtzID0gc2VxKC01MCwgNjAsIDEwKSkgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIG5hbWUgPSAiQnJheS1DdXJ0aXMgZGlzdGFuY2UgdG8gYmFzZWxpbmUiLCAKICAgIGxpbWl0cyA9IGMoTkEsIE5BKSwgYnJlYWtzID0gc2VxKDAuMSwgMC44MCwgMC4xKSkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkpIApgYGAKCgpgYGB7ciBmcGNhLWRlcml2LWJyYXktZGlldCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTYuNX0KZnBjYS5icmF5LmRpZXQzMFtbImZpdHRlZCJdXSA8LSBmcGNhLmJyYXkuZGlldDMwW1siZml0dGVkIl1dICU+JQogIG11dGF0ZSgKICAgIEludGVydmFsID0gaWZlbHNlKGZwY2EuYnJheS5kaWV0MzBbWyJmaXR0ZWQiXV0kdGltZSA8IDAgLCAiUHJlRGlldCIsCiAgICAgICAgICAgICAgIGlmZWxzZShmcGNhLmJyYXkuZGlldDMwW1siZml0dGVkIl1dJHRpbWUgPj0gMCAmIGZwY2EuYnJheS5kaWV0MzBbWyJmaXR0ZWQiXV0kdGltZSA8PSA0LCAiTWlkRGlldCIsCiAgICAgICAgICAgICAgICAgICAgICAiUG9zdERpZXQiKSkpCgoocERpZXREZXJpdiA8LSAgZnBjYS5icmF5LmRpZXQzMFtbImZpdHRlZCJdXSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBSZWxEYXkpKSArCiAgZ2VvbV9saW5lKAogICAgYWVzKGdyb3VwID0gU3ViamVjdCwgeCA9IHRpbWUsIHkgPSBkZXJpdiwgY29sb3IgPSBJbnRlcnZhbCksCiAgICBhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuNykgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGx3ZCA9IDEsIGNvbG9yID0gIm9yYW5nZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBsd2QgPSAxLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGRpZXRfaW50dl9jb2xzLCBuYW1lID0gIkludGVydmFsIikgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIiIsCiAgICBsaW1pdHMgPSBjKE5BLCBOQSksIGJyZWFrcyA9IHNlcSgtNTAsIDYwLCAxMCkpICsKICB5bGFiKCJEZXJpdmF0aXZlIikpCmBgYAoKCmBgYHtyIGZwY2EtYnJheS1kaWV0LWxhYnMsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02LjV9CihwRGlldExhYiA8LSBicmF5X3RvX2Jhc2VsaW5lX2ZsdHIgJT4lIAogIGZpbHRlcihwZXJ0dXJiYXRpb24gPT0gIkRpZXQiLCBSZWxEYXkgPj0gLTUwLCBSZWxEYXkgPD0gNjApICU+JQpnZ3Bsb3QoYWVzKHggPSBSZWxEYXksIHkgPSBkaXN0X3RvX2Jhc2VsaW5lKSkgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBmcGNhLmJyYXkuZGlldFtbImZpdHRlZCJdXSwKICAgIGFlcyhncm91cCA9IFN1YmplY3QsIHggPSB0aW1lLCB5ID0gdmFsdWUpLAogICAgYWxwaGEgPSAwLjMsIHNpemUgPSAwLjcgKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbHdkID0gMSwgY29sb3IgPSAib3JhbmdlIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQsIGx3ZCA9IDEsIGNvbG9yID0gIm9yYW5nZSIpICsKICBnZW9tX3RleHQoCiAgICAgIGFlcyhsYWJlbCA9IFN1YmplY3QsIGNvbG9yID0gSW50ZXJ2YWwpLCBzaXplID0gNCwgYWxwaGEgPSAwLjcpICsKICBnZW9tX2xpbmUoCiAgICBkYXRhID0gZnBjYS5icmF5LmRpZXRbWyJtZWFuIl1dLCBhZXMoeCA9IHRpbWUsIHkgPSB2YWx1ZSksCiAgICBjb2xvciA9ICJuYXZ5Iiwgc2l6ZSA9IDIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZGlldF9pbnR2X2NvbHMsIG5hbWUgPSAiSW50ZXJ2YWwiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgbmFtZSA9ICJEYXlzIGZyb20gaW5pdGlhbCBhbnRpYmlvdGljIGRvc2UiLAogICAgbGltaXRzID0gYyhOQSwgTkEpLCBicmVha3MgPSBzZXEoLTUwLCA2MCwgMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbmFtZSA9ICJCcmF5LUN1cnRpcyBkaXN0YW5jZSB0byBiYXNlbGluZSIsIAogICAgbGltaXRzID0gYygwLjEsIDAuODUpLCBicmVha3MgPSBzZXEoMC4xLCAwLjgwLCAwLjEpKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSkKCmBgYAoKCmBgYHtyIGZwY2EtYnJheS1kaWV0LXZhbC1kZXJpdiwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTYuNX0KdnAgPSBncmlkOjp2aWV3cG9ydCh3aWR0aCA9IDAuMywgaGVpZ2h0ID0gMC4zMiwgeCA9IDAuMDcsIHkgPTAuOTUsIGp1c3QgPSBjKCJsZWZ0IiwgInRvcCIpKQpwcmludChwRGlldCkKcHJpbnQocERpZXREZXJpdiArIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEwKSArIHRoZW1lX3N1YnBsb3QsIHZwID0gdnApCmBgYAoKIyMgQ29sb24gY2xlYW5vdXQKCgoKYGBge3IgYnJheS1jY30KYnJheV90b19iYXNlbGluZV9mbHRyICU+JSAKICBmaWx0ZXIocGVydHVyYmF0aW9uID09ICJDQyIsIFJlbERheSA+PSAtNTAsIFJlbERheSA8PSA1MCkgJT4lCiAgbXV0YXRlKEludGVydmFsID0gZmFjdG9yKEludGVydmFsLCBsZXZlbCA9IG5hbWVzKGNjX2ludHZfY29scykpKSAlPiUKICBnZ3Bsb3QoCiAgICBhZXMoeCA9IFJlbERheSwgeSA9IGRpc3RfdG9fYmFzZWxpbmUsIAogICAgICAgIGdyb3VwID0gU3ViamVjdCwgY29sb3IgPSBJbnRlcnZhbCkpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gU3ViamVjdCksIGFscGhhID0gMC43LCBsd2QgPSAwLjUpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2l6ZSA9IDEuMikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2NfaW50dl9jb2xzKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArIAogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTMpKSkgKwogIHhsYWIoIkRheXMgZnJvbSBkaWV0IGluaXRpYXRpb24iKSArCiAgeWxhYigiQnJheS1DdXJ0aXMgZGlzdGFuY2UgdG8gNyBwcmUtZGlldCBzYW1wbGVzIikgCmBgYAoKCmBgYHtyfQpmcGNhLmJyYXkuY2MzMCA8LSBmaXRfZGlzdF90b19iYXNlbGluZSgKICBicmF5X3RvX2Jhc2VsaW5lX2ZsdHIgJT4lIAogICAgZmlsdGVyKHBlcnR1cmJhdGlvbiA9PSAiQ0MiLCBSZWxEYXkgPj0gLTMwLCBSZWxEYXkgPD0gMzApICU+JQogICAgYXJyYW5nZShTdWJqZWN0LCBSZWxEYXkpKQoKc2F2ZShsaXN0ID0gYygiZnBjYS5icmF5LmFieCIsICJmcGNhLmJyYXkuZGlldCIsImZwY2EuYnJheS5kaWV0MzAiLCAKICAgICAgICAgICAgICAiZnBjYS5icmF5LmNjIiwgImZwY2EuYnJheS5jYzMwIiksIAogICAgIGZpbGUgPSAib3V0cHV0L2ZwY2FfcmVzLnJkYSIpCmBgYAoKYGBge3IgZnBjYS1icmF5LWNjLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Ni41fQoocENDIDwtIGJyYXlfdG9fYmFzZWxpbmVfZmx0ciAlPiUgCiAgZmlsdGVyKHBlcnR1cmJhdGlvbiA9PSAiQ0MiLCBSZWxEYXkgPj0gLTMwLCBSZWxEYXkgPD0gMzApICU+JQpnZ3Bsb3QoYWVzKHggPSBSZWxEYXksIHkgPSBkaXN0X3RvX2Jhc2VsaW5lKSkgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBmcGNhLmJyYXkuY2MzMFtbImZpdHRlZCJdXSwKICAgIGFlcyhncm91cCA9IFN1YmplY3QsIHggPSB0aW1lLCB5ID0gdmFsdWUpLAogICAgYWxwaGEgPSAwLjMsIHNpemUgPSAwLjcsIGNvbG9yID0gImdyZXkzMCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsd2QgPSAxLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNCwgbHdkID0gMSwgY29sb3IgPSAib3JhbmdlIikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gSW50ZXJ2YWwpLCBzaXplID0gMS41LCBhbHBoYSA9IDAuNykgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBmcGNhLmJyYXkuY2MzMFtbIm1lYW4iXV0sIGFlcyh4ID0gdGltZSwgeSA9IHZhbHVlKSwKICAgIGNvbG9yID0gIm5hdnkiLCBzaXplID0gMikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjY19pbnR2X2NvbHMsIG5hbWUgPSAiSW50ZXJ2YWwiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgbmFtZSA9ICJEYXlzIGZyb20gaW5pdGlhbCBhbnRpYmlvdGljIGRvc2UiLAogICAgbGltaXRzID0gYyhOQSwgTkEpLCBicmVha3MgPSBzZXEoLTUwLCA2MCwgMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbmFtZSA9ICJCcmF5LUN1cnRpcyBkaXN0YW5jZSB0byBiYXNlbGluZSIsIAogICAgbGltaXRzID0gYyhOQSwgTkEpLCBicmVha3MgPSBzZXEoMC4xLCAwLjgwLCAwLjEpKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSkgCmBgYAoKCmBgYHtyIGZwY2EtZGVyaXYtYnJheS1jYywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTYuNX0KZnBjYS5icmF5LmNjMzBbWyJmaXR0ZWQiXV0gPC0gZnBjYS5icmF5LmNjMzBbWyJmaXR0ZWQiXV0gJT4lCiAgbXV0YXRlKEludGVydmFsID0gaWZlbHNlKGZwY2EuYnJheS5jYzMwW1siZml0dGVkIl1dJHRpbWUgPCAwICwgIlByZUNDIiwiUG9zdENDIikpCgoocENDRGVyaXYgPC0gIGZwY2EuYnJheS5jYzMwW1siZml0dGVkIl1dICU+JQogIGdncGxvdChhZXMoeCA9IFJlbERheSkpICsKICBnZW9tX2xpbmUoCiAgICBhZXMoZ3JvdXAgPSBTdWJqZWN0LCB4ID0gdGltZSwgeSA9IGRlcml2LCBjb2xvciA9IEludGVydmFsKSwKICAgIGFscGhhID0gMC41LCBzaXplID0gMC43KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbHdkID0gMSwgY29sb3IgPSAib3JhbmdlIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQsIGx3ZCA9IDEsIGNvbG9yID0gIm9yYW5nZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2NfaW50dl9jb2xzLCBuYW1lID0gIkludGVydmFsIikgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIiIsCiAgICBsaW1pdHMgPSBjKE5BLCBOQSksIGJyZWFrcyA9IHNlcSgtNTAsIDYwLCAxMCkpICsKICB5bGFiKCJEZXJpdmF0aXZlIikpCmBgYAoKCmBgYHtyIGZwY2EtYnJheS1jYy1sYWJzLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Ni41fQoocENDTGFiIDwtIGJyYXlfdG9fYmFzZWxpbmVfZmx0ciAlPiUgCiAgZmlsdGVyKHBlcnR1cmJhdGlvbiA9PSAiQ0MiLCBSZWxEYXkgPj0gLTUwLCBSZWxEYXkgPD0gNTApICU+JQpnZ3Bsb3QoYWVzKHggPSBSZWxEYXksIHkgPSBkaXN0X3RvX2Jhc2VsaW5lKSkgKwogIGdlb21fbGluZSgKICAgIGRhdGEgPSBmcGNhLmJyYXkuY2NbWyJmaXR0ZWQiXV0sCiAgICBhZXMoZ3JvdXAgPSBTdWJqZWN0LCB4ID0gdGltZSwgeSA9IHZhbHVlKSwKICAgIGFscGhhID0gMC4zLCBzaXplID0gMC43ICkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGx3ZCA9IDEsIGNvbG9yID0gIm9yYW5nZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0LCBsd2QgPSAxLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgZ2VvbV90ZXh0KAogICAgICBhZXMobGFiZWwgPSBTdWJqZWN0LCBjb2xvciA9IEludGVydmFsKSwgc2l6ZSA9IDQsIGFscGhhID0gMC43KSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IGZwY2EuYnJheS5jY1tbIm1lYW4iXV0sIGFlcyh4ID0gdGltZSwgeSA9IHZhbHVlKSwKICAgIGNvbG9yID0gIm5hdnkiLCBzaXplID0gMikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjY19pbnR2X2NvbHMsIG5hbWUgPSAiSW50ZXJ2YWwiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgbmFtZSA9ICJEYXlzIGZyb20gaW5pdGlhbCBhbnRpYmlvdGljIGRvc2UiLAogICAgbGltaXRzID0gYyhOQSwgTkEpLCBicmVha3MgPSBzZXEoLTUwLCA2MCwgMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbmFtZSA9ICJCcmF5LUN1cnRpcyBkaXN0YW5jZSB0byBiYXNlbGluZSIsIAogICAgbGltaXRzID0gYygwLjEsIDAuODUpLCBicmVha3MgPSBzZXEoMC4xLCAwLjgwLCAwLjEpKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSkKCmBgYAoKCmBgYHtyIGZwY2EtYnJheS1jYy12YWwtZGVyaXYsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02LjV9CnZwID0gZ3JpZDo6dmlld3BvcnQod2lkdGggPSAwLjMsIGhlaWdodCA9IDAuMzIsIHggPSAwLjA3LCB5ID0wLjk1LCBqdXN0ID0gYygibGVmdCIsICJ0b3AiKSkKcHJpbnQocENDKQpwcmludChwQ0NEZXJpdiArIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEwKSArIHRoZW1lX3N1YnBsb3QsIHZwID0gdnApCmBgYAoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=